#define DEBUG
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
using ICSharpCode.TextEditor.Document;

namespace ICSharpCode.TextEditor
{
	public class TextView : AbstractMargin, IDisposable
	{
		private struct MarkerToDraw
		{
			internal TextMarker marker;

			internal RectangleF drawingRect;

			public MarkerToDraw(TextMarker marker, RectangleF drawingRect)
			{
				this.marker = marker;
				this.drawingRect = drawingRect;
			}
		}

		private struct WordFontPair
		{
			private string word;

			private Font font;

			public WordFontPair(string word, Font font)
			{
				this.word = word;
				this.font = font;
			}

			public override bool Equals(object obj)
			{
				WordFontPair wordFontPair = (WordFontPair)obj;
				if (!this.word.Equals(wordFontPair.word))
				{
					return false;
				}
				return this.font.Equals(wordFontPair.font);
			}

			public override int GetHashCode()
			{
				return this.word.GetHashCode() ^ this.font.GetHashCode();
			}
		}

		private int fontHeight;

		private Highlight highlight;

		private int physicalColumn = 0;

		private int spaceWidth;

		private int wideSpaceWidth;

		private Font lastFont;

		private const int additionalFoldTextSize = 1;

		private List<MarkerToDraw> markersToDraw = new List<MarkerToDraw>();

		private Dictionary<WordFontPair, int> measureCache = new Dictionary<WordFontPair, int>();

		private const int MaximumWordLength = 1000;

		private const int MaximumCacheSize = 2000;

		private const TextFormatFlags textFormatFlags = TextFormatFlags.NoPrefix | TextFormatFlags.PreserveGraphicsClipping | TextFormatFlags.NoPadding;

		private Dictionary<Font, Dictionary<char, int>> fontBoundCharWidth = new Dictionary<Font, Dictionary<char, int>>();

		private const int MinTabWidth = 4;

		public Highlight Highlight
		{
			get
			{
				return this.highlight;
			}
			set
			{
				this.highlight = value;
			}
		}

		public int FirstPhysicalLine => base.textArea.VirtualTop.Y / this.fontHeight;

		public int LineHeightRemainder => base.textArea.VirtualTop.Y % this.fontHeight;

		public int FirstVisibleLine
		{
			get
			{
				return base.textArea.Document.GetFirstLogicalLine(base.textArea.VirtualTop.Y / this.fontHeight);
			}
			set
			{
				if (this.FirstVisibleLine != value)
				{
					base.textArea.VirtualTop = new Point(base.textArea.VirtualTop.X, base.textArea.Document.GetVisibleLine(value) * this.fontHeight);
				}
			}
		}

		public int VisibleLineDrawingRemainder => base.textArea.VirtualTop.Y % this.fontHeight;

		public int FontHeight => this.fontHeight;

		public int VisibleLineCount => 1 + base.DrawingPosition.Height / this.fontHeight;

		public int VisibleColumnCount => base.DrawingPosition.Width / this.WideSpaceWidth - 1;

		public int SpaceWidth => this.spaceWidth;

		public int WideSpaceWidth => this.wideSpaceWidth;

		public void Dispose()
		{
			this.measureCache.Clear();
		}

		public TextView(TextArea textArea)
			: base(textArea)
		{
			base.Cursor = Cursors.IBeam;
			this.OptionsChanged();
		}

		private static int GetFontHeight(Font font)
		{
			int height = TextRenderer.MeasureText("_", font).Height;
			int val = (int)Math.Ceiling(font.GetHeight());
			return Math.Max(height, val) + 1;
		}

		public void OptionsChanged()
		{
			this.lastFont = base.TextEditorProperties.FontContainer.RegularFont;
			this.fontHeight = TextView.GetFontHeight(this.lastFont);
			this.spaceWidth = Math.Max(this.GetWidth(' ', this.lastFont), 1);
			this.wideSpaceWidth = Math.Max(this.spaceWidth, this.GetWidth('x', this.lastFont));
		}

		public override void Paint(Graphics g, Rectangle rect)
		{
			if (rect.Width <= 0 || rect.Height <= 0)
			{
				return;
			}
			if (this.lastFont != base.TextEditorProperties.FontContainer.RegularFont)
			{
				this.OptionsChanged();
				base.textArea.Invalidate();
			}
			int x = base.textArea.VirtualTop.X;
			if (x > 0)
			{
				g.SetClip(base.DrawingPosition);
			}
			for (int i = 0; i < (base.DrawingPosition.Height + this.VisibleLineDrawingRemainder) / this.fontHeight + 1; i++)
			{
				Rectangle rectangle = new Rectangle(base.DrawingPosition.X - x, base.DrawingPosition.Top + i * this.fontHeight - this.VisibleLineDrawingRemainder, base.DrawingPosition.Width + x, this.fontHeight);
				if (rect.IntersectsWith(rectangle))
				{
					int visibleLine = base.textArea.Document.GetVisibleLine(this.FirstVisibleLine);
					int firstLogicalLine = base.textArea.Document.GetFirstLogicalLine(base.textArea.Document.GetVisibleLine(this.FirstVisibleLine) + i);
					this.PaintDocumentLine(g, firstLogicalLine, rectangle);
				}
			}
			this.DrawMarkerDraw(g);
			if (x > 0)
			{
				g.ResetClip();
			}
			base.textArea.Caret.PaintCaret(g);
		}

		private void PaintDocumentLine(Graphics g, int lineNumber, Rectangle lineRectangle)
		{
			Debug.Assert(lineNumber >= 0);
			Brush bgColorBrush = this.GetBgColorBrush(lineNumber);
			Brush brush = (base.textArea.Enabled ? bgColorBrush : SystemBrushes.InactiveBorder);
			if (lineNumber >= base.textArea.Document.TotalNumberOfLines)
			{
				g.FillRectangle(brush, lineRectangle);
				if (base.TextEditorProperties.ShowInvalidLines)
				{
					this.DrawInvalidLineMarker(g, lineRectangle.Left, lineRectangle.Top);
				}
				if (base.TextEditorProperties.ShowVerticalRuler)
				{
					this.DrawVerticalRuler(g, lineRectangle);
				}
				return;
			}
			int num = lineRectangle.X;
			int num2 = 0;
			this.physicalColumn = 0;
			if (base.TextEditorProperties.EnableFolding)
			{
				while (true)
				{
					List<FoldMarker> foldedFoldingsWithStartAfterColumn = base.textArea.Document.FoldingManager.GetFoldedFoldingsWithStartAfterColumn(lineNumber, num2 - 1);
					if (foldedFoldingsWithStartAfterColumn == null || foldedFoldingsWithStartAfterColumn.Count <= 0)
					{
						if (lineNumber < base.textArea.Document.TotalNumberOfLines)
						{
							num = this.PaintLinePart(g, lineNumber, num2, base.textArea.Document.GetLineSegment(lineNumber).Length, lineRectangle, num);
						}
						break;
					}
					FoldMarker foldMarker = foldedFoldingsWithStartAfterColumn[0];
					foreach (FoldMarker item in foldedFoldingsWithStartAfterColumn)
					{
						if (item.StartColumn < foldMarker.StartColumn)
						{
							foldMarker = item;
						}
					}
					foldedFoldingsWithStartAfterColumn.Clear();
					num = this.PaintLinePart(g, lineNumber, num2, foldMarker.StartColumn, lineRectangle, num);
					num2 = foldMarker.EndColumn;
					lineNumber = foldMarker.EndLine;
					if (lineNumber >= base.textArea.Document.TotalNumberOfLines)
					{
						Debug.Assert(condition: false, "Folding ends after document end");
						break;
					}
					ColumnRange selectionAtLine = base.textArea.SelectionManager.GetSelectionAtLine(lineNumber);
					bool drawSelected = ColumnRange.WholeColumn.Equals(selectionAtLine) || (foldMarker.StartColumn >= selectionAtLine.StartColumn && foldMarker.EndColumn <= selectionAtLine.EndColumn);
					num = this.PaintFoldingText(g, lineNumber, num, lineRectangle, foldMarker.FoldText, drawSelected);
				}
			}
			else
			{
				num = this.PaintLinePart(g, lineNumber, 0, base.textArea.Document.GetLineSegment(lineNumber).Length, lineRectangle, num);
			}
			if (lineNumber < base.textArea.Document.TotalNumberOfLines)
			{
				ColumnRange selectionAtLine2 = base.textArea.SelectionManager.GetSelectionAtLine(lineNumber);
				LineSegment lineSegment = base.textArea.Document.GetLineSegment(lineNumber);
				HighlightColor colorFor = base.textArea.Document.HighlightingStrategy.GetColorFor("Selection");
				bool flag = selectionAtLine2.EndColumn > lineSegment.Length || ColumnRange.WholeColumn.Equals(selectionAtLine2);
				if (base.TextEditorProperties.ShowEOLMarker)
				{
					HighlightColor colorFor2 = base.textArea.Document.HighlightingStrategy.GetColorFor("EOLMarkers");
					num += this.DrawEOLMarker(g, colorFor2.Color, flag ? bgColorBrush : brush, num, lineRectangle.Y);
				}
				else if (flag)
				{
					g.FillRectangle(BrushRegistry.GetBrush(colorFor.BackgroundColor), new RectangleF(num, lineRectangle.Y, this.WideSpaceWidth, lineRectangle.Height));
					num += this.WideSpaceWidth;
				}
				Brush brush2 = ((flag && base.TextEditorProperties.AllowCaretBeyondEOL) ? bgColorBrush : brush);
				g.FillRectangle(brush2, new RectangleF(num, lineRectangle.Y, lineRectangle.Width - num + lineRectangle.X, lineRectangle.Height));
			}
			if (base.TextEditorProperties.ShowVerticalRuler)
			{
				this.DrawVerticalRuler(g, lineRectangle);
			}
		}

		private bool DrawLineMarkerAtLine(int lineNumber)
		{
			return lineNumber == base.textArea.Caret.Line && base.textArea.MotherTextAreaControl.TextEditorProperties.LineViewerStyle == LineViewerStyle.FullRow;
		}

		private Brush GetBgColorBrush(int lineNumber)
		{
			if (this.DrawLineMarkerAtLine(lineNumber))
			{
				HighlightColor colorFor = base.textArea.Document.HighlightingStrategy.GetColorFor("CaretMarker");
				return BrushRegistry.GetBrush(colorFor.Color);
			}
			HighlightColor colorFor2 = base.textArea.Document.HighlightingStrategy.GetColorFor("Default");
			Color backgroundColor = colorFor2.BackgroundColor;
			return BrushRegistry.GetBrush(backgroundColor);
		}

		private int PaintFoldingText(Graphics g, int lineNumber, int physicalXPos, Rectangle lineRectangle, string text, bool drawSelected)
		{
			HighlightColor colorFor = base.textArea.Document.HighlightingStrategy.GetColorFor("Selection");
			Brush brush = (drawSelected ? BrushRegistry.GetBrush(colorFor.BackgroundColor) : this.GetBgColorBrush(lineNumber));
			Brush brush2 = (base.textArea.Enabled ? brush : SystemBrushes.InactiveBorder);
			Font regularFont = base.textArea.TextEditorProperties.FontContainer.RegularFont;
			int num = this.MeasureStringWidth(g, text, regularFont) + 1;
			Rectangle rect = new Rectangle(physicalXPos, lineRectangle.Y, num, lineRectangle.Height - 1);
			g.FillRectangle(brush2, rect);
			this.physicalColumn += text.Length;
			this.DrawString(g, text, regularFont, drawSelected ? colorFor.Color : Color.Gray, rect.X + 1, rect.Y);
			g.DrawRectangle(BrushRegistry.GetPen(drawSelected ? Color.DarkGray : Color.Gray), rect.X, rect.Y, rect.Width, rect.Height);
			return physicalXPos + num + 1;
		}

		private void DrawMarker(Graphics g, TextMarker marker, RectangleF drawingRect)
		{
			this.markersToDraw.Add(new MarkerToDraw(marker, drawingRect));
		}

		private void DrawMarkerDraw(Graphics g)
		{
			foreach (MarkerToDraw item in this.markersToDraw)
			{
				TextMarker marker = item.marker;
				RectangleF drawingRect = item.drawingRect;
				float num = drawingRect.Bottom - 1f;
				switch (marker.TextMarkerType)
				{
				case TextMarkerType.Underlined:
					g.DrawLine(BrushRegistry.GetPen(marker.Color), drawingRect.X, num, drawingRect.Right, num);
					break;
				case TextMarkerType.WaveLine:
				{
					int num2 = (int)drawingRect.X % 6;
					for (float num3 = (int)drawingRect.X - num2; num3 < drawingRect.Right; num3 += 6f)
					{
						g.DrawLine(BrushRegistry.GetPen(marker.Color), num3, num + 3f - 4f, num3 + 3f, num + 1f - 4f);
						if (num3 + 3f < drawingRect.Right)
						{
							g.DrawLine(BrushRegistry.GetPen(marker.Color), num3 + 3f, num + 1f - 4f, num3 + 6f, num + 3f - 4f);
						}
					}
					break;
				}
				case TextMarkerType.SolidBlock:
					g.FillRectangle(BrushRegistry.GetBrush(marker.Color), drawingRect);
					break;
				}
			}
			this.markersToDraw.Clear();
		}

		private Brush GetMarkerBrushAt(int offset, int length, ref Color foreColor, out IList<TextMarker> markers)
		{
			markers = base.Document.MarkerStrategy.GetMarkers(offset, length);
			foreach (TextMarker marker in markers)
			{
				if (marker.TextMarkerType == TextMarkerType.SolidBlock)
				{
					if (marker.OverrideForeColor)
					{
						foreColor = marker.ForeColor;
					}
					return BrushRegistry.GetBrush(marker.Color);
				}
			}
			return null;
		}

		private int PaintLinePart(Graphics g, int lineNumber, int startColumn, int endColumn, Rectangle lineRectangle, int physicalXPos)
		{
			bool flag = this.DrawLineMarkerAtLine(lineNumber);
			Brush brush = (base.textArea.Enabled ? this.GetBgColorBrush(lineNumber) : SystemBrushes.InactiveBorder);
			HighlightColor colorFor = base.textArea.Document.HighlightingStrategy.GetColorFor("Selection");
			ColumnRange selectionAtLine = base.textArea.SelectionManager.GetSelectionAtLine(lineNumber);
			HighlightColor colorFor2 = base.textArea.Document.HighlightingStrategy.GetColorFor("TabMarkers");
			HighlightColor colorFor3 = base.textArea.Document.HighlightingStrategy.GetColorFor("SpaceMarkers");
			LineSegment lineSegment = base.textArea.Document.GetLineSegment(lineNumber);
			Brush brush2 = BrushRegistry.GetBrush(colorFor.BackgroundColor);
			if (lineSegment.Words == null)
			{
				return physicalXPos;
			}
			int num = 0;
			TextWord textWord = null;
			FontContainer fontContainer = base.TextEditorProperties.FontContainer;
			for (int i = 0; i < lineSegment.Words.Count; i++)
			{
				TextWord word = lineSegment.Words[i];
				if (num < startColumn)
				{
					num += word.Length;
					continue;
				}
				while (num < endColumn && physicalXPos < lineRectangle.Right)
				{
					int num2 = num + word.Length - 1;
					Color foreColor = word.Type switch
					{
						TextWordType.Space => colorFor3.Color, 
						TextWordType.Tab => colorFor2.Color, 
						_ => word.Color, 
					};
					IList<TextMarker> markers;
					Brush brush3 = this.GetMarkerBrushAt(lineSegment.Offset + num, word.Length, ref foreColor, out markers);
					if (word.Length > 1)
					{
						int num3 = int.MaxValue;
						if (this.highlight != null)
						{
							if (this.highlight.OpenBrace.Y == lineNumber && this.highlight.OpenBrace.X >= num && this.highlight.OpenBrace.X <= num2)
							{
								num3 = Math.Min(num3, this.highlight.OpenBrace.X - num);
							}
							if (this.highlight.CloseBrace.Y == lineNumber && this.highlight.CloseBrace.X >= num && this.highlight.CloseBrace.X <= num2)
							{
								num3 = Math.Min(num3, this.highlight.CloseBrace.X - num);
							}
							if (num3 == 0)
							{
								num3 = 1;
							}
						}
						if (endColumn < num2)
						{
							num3 = Math.Min(num3, endColumn - num);
						}
						if (selectionAtLine.StartColumn > num && selectionAtLine.StartColumn <= num2)
						{
							num3 = Math.Min(num3, selectionAtLine.StartColumn - num);
						}
						else if (selectionAtLine.EndColumn > num && selectionAtLine.EndColumn <= num2)
						{
							num3 = Math.Min(num3, selectionAtLine.EndColumn - num);
						}
						foreach (TextMarker item in markers)
						{
							int num4 = item.Offset - lineSegment.Offset;
							int num5 = item.EndOffset - lineSegment.Offset + 1;
							if (num4 > num && num4 <= num2)
							{
								num3 = Math.Min(num3, num4 - num);
							}
							else if (num5 > num && num5 <= num2)
							{
								num3 = Math.Min(num3, num5 - num);
							}
						}
						if (num3 != int.MaxValue)
						{
							if (textWord != null)
							{
								throw new ApplicationException("split part invalid: first part cannot be splitted further");
							}
							textWord = TextWord.Split(ref word, num3);
							continue;
						}
					}
					if (ColumnRange.WholeColumn.Equals(selectionAtLine) || (selectionAtLine.StartColumn <= num && selectionAtLine.EndColumn > num2))
					{
						brush3 = brush2;
						if (colorFor.HasForeground)
						{
							foreColor = colorFor.Color;
						}
					}
					else if (flag)
					{
						brush3 = brush;
					}
					if (brush3 == null)
					{
						brush3 = ((word.SyntaxColor == null || !word.SyntaxColor.HasBackground) ? brush : BrushRegistry.GetBrush(word.SyntaxColor.BackgroundColor));
					}
					RectangleF rectangleF;
					if (word.Type == TextWordType.Space)
					{
						this.physicalColumn++;
						rectangleF = new RectangleF(physicalXPos, lineRectangle.Y, this.SpaceWidth, lineRectangle.Height);
						g.FillRectangle(brush3, rectangleF);
						if (base.TextEditorProperties.ShowSpaces)
						{
							this.DrawSpaceMarker(g, foreColor, physicalXPos, lineRectangle.Y);
						}
						physicalXPos += this.SpaceWidth;
					}
					else if (word.Type == TextWordType.Tab)
					{
						this.physicalColumn += base.TextEditorProperties.TabIndent;
						this.physicalColumn = this.physicalColumn / base.TextEditorProperties.TabIndent * base.TextEditorProperties.TabIndent;
						int num6 = (physicalXPos + 4 - lineRectangle.X) / this.WideSpaceWidth / base.TextEditorProperties.TabIndent * this.WideSpaceWidth * base.TextEditorProperties.TabIndent + lineRectangle.X;
						num6 += this.WideSpaceWidth * base.TextEditorProperties.TabIndent;
						rectangleF = new RectangleF(physicalXPos, lineRectangle.Y, num6 - physicalXPos, lineRectangle.Height);
						g.FillRectangle(brush3, rectangleF);
						if (base.TextEditorProperties.ShowTabs)
						{
							this.DrawTabMarker(g, foreColor, physicalXPos, lineRectangle.Y);
						}
						physicalXPos = num6;
					}
					else
					{
						int num7 = this.DrawDocumentWord(g, word.Word, new Point(physicalXPos, lineRectangle.Y), word.GetFont(fontContainer), foreColor, brush3);
						rectangleF = new RectangleF(physicalXPos, lineRectangle.Y, num7, lineRectangle.Height);
						physicalXPos += num7;
					}
					foreach (TextMarker item2 in markers)
					{
						if (item2.TextMarkerType != TextMarkerType.SolidBlock)
						{
							this.DrawMarker(g, item2, rectangleF);
						}
					}
					if (this.highlight != null && ((this.highlight.OpenBrace.Y == lineNumber && this.highlight.OpenBrace.X == num) || (this.highlight.CloseBrace.Y == lineNumber && this.highlight.CloseBrace.X == num)))
					{
						this.DrawBracketHighlight(g, new Rectangle((int)rectangleF.X, lineRectangle.Y, (int)rectangleF.Width - 1, lineRectangle.Height - 1));
					}
					num += word.Length;
					if (textWord != null)
					{
						word = textWord;
						textWord = null;
						continue;
					}
					goto IL_07ca;
				}
				break;
				IL_07ca:;
			}
			if (physicalXPos < lineRectangle.Right && endColumn >= lineSegment.Length)
			{
				IList<TextMarker> markers2 = base.Document.MarkerStrategy.GetMarkers(lineSegment.Offset + lineSegment.Length);
				foreach (TextMarker item3 in markers2)
				{
					if (item3.TextMarkerType != TextMarkerType.SolidBlock)
					{
						this.DrawMarker(g, item3, new RectangleF(physicalXPos, lineRectangle.Y, this.WideSpaceWidth, lineRectangle.Height));
					}
				}
			}
			return physicalXPos;
		}

		private int DrawDocumentWord(Graphics g, string word, Point position, Font font, Color foreColor, Brush backBrush)
		{
			if (word == null || word.Length == 0)
			{
				return 0;
			}
			if (word.Length > 1000)
			{
				int num = 0;
				for (int i = 0; i < word.Length; i += 1000)
				{
					Point position2 = position;
					position2.X += num;
					num = ((i + 1000 >= word.Length) ? (num + this.DrawDocumentWord(g, word.Substring(i, word.Length - i), position2, font, foreColor, backBrush)) : (num + this.DrawDocumentWord(g, word.Substring(i, 1000), position2, font, foreColor, backBrush)));
				}
				return num;
			}
			int num2 = this.MeasureStringWidth(g, word, font);
			g.FillRectangle(backBrush, new RectangleF(position.X, position.Y, num2 + 1, this.FontHeight));
			this.DrawString(g, word, font, foreColor, position.X, position.Y);
			return num2;
		}

		private int MeasureStringWidth(Graphics g, string word, Font font)
		{
			if (word == null || word.Length == 0)
			{
				return 0;
			}
			int num;
			if (word.Length > 1000)
			{
				num = 0;
				for (int i = 0; i < word.Length; i += 1000)
				{
					num = ((i + 1000 >= word.Length) ? (num + this.MeasureStringWidth(g, word.Substring(i, word.Length - i), font)) : (num + this.MeasureStringWidth(g, word.Substring(i, 1000), font)));
				}
				return num;
			}
			if (this.measureCache.TryGetValue(new WordFontPair(word, font), out num))
			{
				return num;
			}
			if (this.measureCache.Count > 2000)
			{
				this.measureCache.Clear();
			}
			num = TextRenderer.MeasureText(g, word, font, new Size(32767, 32767), TextFormatFlags.NoPrefix | TextFormatFlags.PreserveGraphicsClipping | TextFormatFlags.NoPadding).Width;
			this.measureCache.Add(new WordFontPair(word, font), num);
			return num;
		}

		public int GetWidth(char ch, Font font)
		{
			if (!this.fontBoundCharWidth.ContainsKey(font))
			{
				this.fontBoundCharWidth.Add(font, new Dictionary<char, int>());
			}
			if (!this.fontBoundCharWidth[font].ContainsKey(ch))
			{
				using (Graphics g = base.textArea.CreateGraphics())
				{
					return this.GetWidth(g, ch, font);
				}
			}
			return this.fontBoundCharWidth[font][ch];
		}

		public int GetWidth(Graphics g, char ch, Font font)
		{
			if (!this.fontBoundCharWidth.ContainsKey(font))
			{
				this.fontBoundCharWidth.Add(font, new Dictionary<char, int>());
			}
			if (!this.fontBoundCharWidth[font].ContainsKey(ch))
			{
				this.fontBoundCharWidth[font].Add(ch, this.MeasureStringWidth(g, ch.ToString(), font));
			}
			return this.fontBoundCharWidth[font][ch];
		}

		public int GetVisualColumn(int logicalLine, int logicalColumn)
		{
			int column = 0;
			using (Graphics g = base.textArea.CreateGraphics())
			{
				this.CountColumns(ref column, 0, logicalColumn, logicalLine, g);
			}
			return column;
		}

		public int GetVisualColumnFast(LineSegment line, int logicalColumn)
		{
			int offset = line.Offset;
			int tabIndent = base.Document.TextEditorProperties.TabIndent;
			int num = 0;
			for (int i = 0; i < logicalColumn; i++)
			{
				char c = ((i < line.Length) ? base.Document.GetCharAt(offset + i) : ' ');
				char c2 = c;
				char c3 = c2;
				if (c3 == '\t')
				{
					num += tabIndent;
					num = num / tabIndent * tabIndent;
				}
				else
				{
					num++;
				}
			}
			return num;
		}

		public TextLocation GetLogicalPosition(Point mousePosition)
		{
			FoldMarker inFoldMarker;
			return this.GetLogicalColumn(this.GetLogicalLine(mousePosition.Y), mousePosition.X, out inFoldMarker);
		}

		public TextLocation GetLogicalPosition(int visualPosX, int visualPosY)
		{
			FoldMarker inFoldMarker;
			return this.GetLogicalColumn(this.GetLogicalLine(visualPosY), visualPosX, out inFoldMarker);
		}

		public FoldMarker GetFoldMarkerFromPosition(int visualPosX, int visualPosY)
		{
			this.GetLogicalColumn(this.GetLogicalLine(visualPosY), visualPosX, out var inFoldMarker);
			return inFoldMarker;
		}

		public int GetLogicalLine(int visualPosY)
		{
			int lineNumber = Math.Max(0, (visualPosY + base.textArea.VirtualTop.Y) / this.fontHeight);
			return base.Document.GetFirstLogicalLine(lineNumber);
		}

		internal TextLocation GetLogicalColumn(int lineNumber, int visualPosX, out FoldMarker inFoldMarker)
		{
			visualPosX += base.textArea.VirtualTop.X;
			inFoldMarker = null;
			if (lineNumber >= base.Document.TotalNumberOfLines)
			{
				return new TextLocation(visualPosX / this.WideSpaceWidth, lineNumber);
			}
			if (visualPosX <= 0)
			{
				return new TextLocation(0, lineNumber);
			}
			int num = 0;
			int drawingPos = 0;
			int logicalColumnInternal;
			using (Graphics g = base.textArea.CreateGraphics())
			{
				while (true)
				{
					LineSegment lineSegment = base.Document.GetLineSegment(lineNumber);
					FoldMarker foldMarker = this.FindNextFoldedFoldingOnLineAfterColumn(lineNumber, num - 1);
					int num2 = foldMarker?.StartColumn ?? int.MaxValue;
					logicalColumnInternal = this.GetLogicalColumnInternal(g, lineSegment, num, num2, ref drawingPos, visualPosX);
					if (logicalColumnInternal < num2)
					{
						break;
					}
					lineNumber = foldMarker.EndLine;
					num = foldMarker.EndColumn;
					int num3 = drawingPos + 1 + this.MeasureStringWidth(g, foldMarker.FoldText, base.TextEditorProperties.FontContainer.RegularFont);
					if (num3 >= visualPosX)
					{
						inFoldMarker = foldMarker;
						if (TextView.IsNearerToAThanB(visualPosX, drawingPos, num3))
						{
							return new TextLocation(foldMarker.StartColumn, foldMarker.StartLine);
						}
						return new TextLocation(foldMarker.EndColumn, foldMarker.EndLine);
					}
					drawingPos = num3;
				}
			}
			return new TextLocation(logicalColumnInternal, lineNumber);
		}

		private int GetLogicalColumnInternal(Graphics g, LineSegment line, int start, int end, ref int drawingPos, int targetVisualPosX)
		{
			if (start == end)
			{
				return end;
			}
			Debug.Assert(start < end);
			Debug.Assert(drawingPos < targetVisualPosX);
			int tabIndent = base.Document.TextEditorProperties.TabIndent;
			FontContainer fontContainer = base.TextEditorProperties.FontContainer;
			List<TextWord> words = line.Words;
			if (words == null)
			{
				return 0;
			}
			int num = 0;
			for (int i = 0; i < words.Count; i++)
			{
				TextWord textWord = words[i];
				if (num >= end)
				{
					return num;
				}
				if (num + textWord.Length >= start)
				{
					int num3;
					switch (textWord.Type)
					{
					case TextWordType.Space:
						num3 = drawingPos + this.spaceWidth;
						if (num3 >= targetVisualPosX)
						{
							return TextView.IsNearerToAThanB(targetVisualPosX, drawingPos, num3) ? num : (num + 1);
						}
						break;
					case TextWordType.Tab:
						drawingPos = (drawingPos + 4) / tabIndent / this.WideSpaceWidth * tabIndent * this.WideSpaceWidth;
						num3 = drawingPos + tabIndent * this.WideSpaceWidth;
						if (num3 >= targetVisualPosX)
						{
							return TextView.IsNearerToAThanB(targetVisualPosX, drawingPos, num3) ? num : (num + 1);
						}
						break;
					case TextWordType.Word:
					{
						int num2 = Math.Max(num, start);
						int length = Math.Min(num + textWord.Length, end) - num2;
						string text = base.Document.GetText(line.Offset + num2, length);
						Font font = textWord.GetFont(fontContainer) ?? fontContainer.RegularFont;
						num3 = drawingPos + this.MeasureStringWidth(g, text, font);
						if (num3 < targetVisualPosX)
						{
							break;
						}
						for (int j = 0; j < text.Length; j++)
						{
							num3 = drawingPos + this.MeasureStringWidth(g, text[j].ToString(), font);
							if (num3 >= targetVisualPosX)
							{
								if (TextView.IsNearerToAThanB(targetVisualPosX, drawingPos, num3))
								{
									return num2 + j;
								}
								return num2 + j + 1;
							}
							drawingPos = num3;
						}
						return num2 + text.Length;
					}
					default:
						throw new NotSupportedException();
					}
					drawingPos = num3;
				}
				num += textWord.Length;
			}
			return num;
		}

		private static bool IsNearerToAThanB(int num, int a, int b)
		{
			return Math.Abs(a - num) < Math.Abs(b - num);
		}

		private FoldMarker FindNextFoldedFoldingOnLineAfterColumn(int lineNumber, int column)
		{
			List<FoldMarker> foldedFoldingsWithStartAfterColumn = base.Document.FoldingManager.GetFoldedFoldingsWithStartAfterColumn(lineNumber, column);
			if (foldedFoldingsWithStartAfterColumn.Count != 0)
			{
				return foldedFoldingsWithStartAfterColumn[0];
			}
			return null;
		}

		private float CountColumns(ref int column, int start, int end, int logicalLine, Graphics g)
		{
			if (start > end)
			{
				throw new ArgumentException("start > end");
			}
			if (start == end)
			{
				return 0f;
			}
			float num = this.SpaceWidth;
			float num2 = 0f;
			int tabIndent = base.Document.TextEditorProperties.TabIndent;
			LineSegment lineSegment = base.Document.GetLineSegment(logicalLine);
			List<TextWord> words = lineSegment.Words;
			if (words == null)
			{
				return 0f;
			}
			int count = words.Count;
			int num3 = 0;
			FontContainer fontContainer = base.TextEditorProperties.FontContainer;
			for (int i = 0; i < count; i++)
			{
				TextWord textWord = words[i];
				if (num3 >= end)
				{
					break;
				}
				if (num3 + textWord.Length >= start)
				{
					switch (textWord.Type)
					{
					case TextWordType.Space:
						num2 += num;
						break;
					case TextWordType.Tab:
						num2 = (int)((num2 + 4f) / (float)tabIndent / (float)this.WideSpaceWidth) * tabIndent * this.WideSpaceWidth;
						num2 += (float)(tabIndent * this.WideSpaceWidth);
						break;
					case TextWordType.Word:
					{
						int num4 = Math.Max(num3, start);
						int length = Math.Min(num3 + textWord.Length, end) - num4;
						string text = base.Document.GetText(lineSegment.Offset + num4, length);
						num2 += (float)this.MeasureStringWidth(g, text, textWord.GetFont(fontContainer) ?? fontContainer.RegularFont);
						break;
					}
					}
				}
				num3 += textWord.Length;
			}
			for (int j = lineSegment.Length; j < end; j++)
			{
				num2 += (float)this.WideSpaceWidth;
			}
			column += (int)((num2 + 1f) / (float)this.WideSpaceWidth);
			return num2;
		}

		public int GetDrawingXPos(int logicalLine, int logicalColumn)
		{
			List<FoldMarker> topLevelFoldedFoldings = base.Document.FoldingManager.GetTopLevelFoldedFoldings();
			FoldMarker foldMarker = null;
			int num;
			for (num = topLevelFoldedFoldings.Count - 1; num >= 0; num--)
			{
				foldMarker = topLevelFoldedFoldings[num];
				if (foldMarker.StartLine < logicalLine || (foldMarker.StartLine == logicalLine && foldMarker.StartColumn < logicalColumn))
				{
					break;
				}
				FoldMarker foldMarker2 = topLevelFoldedFoldings[num / 2];
				if (foldMarker2.StartLine > logicalLine || (foldMarker2.StartLine == logicalLine && foldMarker2.StartColumn >= logicalColumn))
				{
					num /= 2;
				}
			}
			int num2 = 0;
			int num3 = 0;
			int column = 0;
			int tabIndent = base.Document.TextEditorProperties.TabIndent;
			Graphics graphics = base.textArea.CreateGraphics();
			float num4;
			if (foldMarker == null || (foldMarker.StartLine >= logicalLine && (foldMarker.StartLine != logicalLine || foldMarker.StartColumn >= logicalColumn)))
			{
				num4 = this.CountColumns(ref column, 0, logicalColumn, logicalLine, graphics);
				return (int)(num4 - (float)base.textArea.VirtualTop.X);
			}
			if (foldMarker.EndLine > logicalLine || (foldMarker.EndLine == logicalLine && foldMarker.EndColumn > logicalColumn))
			{
				logicalColumn = foldMarker.StartColumn;
				logicalLine = foldMarker.StartLine;
				num--;
			}
			num2 = num;
			while (num >= 0)
			{
				foldMarker = topLevelFoldedFoldings[num];
				if (foldMarker.EndLine < logicalLine)
				{
					break;
				}
				num--;
			}
			num3 = num + 1;
			if (num2 < num3)
			{
				num4 = this.CountColumns(ref column, 0, logicalColumn, logicalLine, graphics);
				return (int)(num4 - (float)base.textArea.VirtualTop.X);
			}
			int start = 0;
			num4 = 0f;
			for (num = num3; num <= num2; num++)
			{
				foldMarker = topLevelFoldedFoldings[num];
				num4 += this.CountColumns(ref column, start, foldMarker.StartColumn, foldMarker.StartLine, graphics);
				start = foldMarker.EndColumn;
				column += foldMarker.FoldText.Length;
				num4 += 1f;
				num4 += (float)this.MeasureStringWidth(graphics, foldMarker.FoldText, base.TextEditorProperties.FontContainer.RegularFont);
			}
			num4 += this.CountColumns(ref column, start, logicalColumn, logicalLine, graphics);
			graphics.Dispose();
			return (int)(num4 - (float)base.textArea.VirtualTop.X);
		}

		private void DrawBracketHighlight(Graphics g, Rectangle rect)
		{
			g.FillRectangle(BrushRegistry.GetBrush(Color.FromArgb(50, 0, 0, 255)), rect);
			g.DrawRectangle(Pens.Blue, rect);
		}

		private void DrawString(Graphics g, string text, Font font, Color color, int x, int y)
		{
			TextRenderer.DrawText(g, text, font, new Point(x, y), color, TextFormatFlags.NoPrefix | TextFormatFlags.PreserveGraphicsClipping | TextFormatFlags.NoPadding);
		}

		private void DrawInvalidLineMarker(Graphics g, int x, int y)
		{
			HighlightColor colorFor = base.textArea.Document.HighlightingStrategy.GetColorFor("InvalidLines");
			this.DrawString(g, "~", colorFor.GetFont(base.TextEditorProperties.FontContainer), colorFor.Color, x, y);
		}

		private void DrawSpaceMarker(Graphics g, Color color, int x, int y)
		{
			HighlightColor colorFor = base.textArea.Document.HighlightingStrategy.GetColorFor("SpaceMarkers");
			this.DrawString(g, "·", colorFor.GetFont(base.TextEditorProperties.FontContainer), color, x, y);
		}

		private void DrawTabMarker(Graphics g, Color color, int x, int y)
		{
			HighlightColor colorFor = base.textArea.Document.HighlightingStrategy.GetColorFor("TabMarkers");
			this.DrawString(g, "»", colorFor.GetFont(base.TextEditorProperties.FontContainer), color, x, y);
		}

		private int DrawEOLMarker(Graphics g, Color color, Brush backBrush, int x, int y)
		{
			HighlightColor colorFor = base.textArea.Document.HighlightingStrategy.GetColorFor("EOLMarkers");
			int width = this.GetWidth('¶', colorFor.GetFont(base.TextEditorProperties.FontContainer));
			g.FillRectangle(backBrush, new RectangleF(x, y, width, this.fontHeight));
			this.DrawString(g, "¶", colorFor.GetFont(base.TextEditorProperties.FontContainer), color, x, y);
			return width;
		}

		private void DrawVerticalRuler(Graphics g, Rectangle lineRectangle)
		{
			int num = this.WideSpaceWidth * base.TextEditorProperties.VerticalRulerRow - base.textArea.VirtualTop.X;
			if (num > 0)
			{
				HighlightColor colorFor = base.textArea.Document.HighlightingStrategy.GetColorFor("VRuler");
				g.DrawLine(BrushRegistry.GetPen(colorFor.Color), base.drawingPosition.Left + num, lineRectangle.Top, base.drawingPosition.Left + num, lineRectangle.Bottom);
			}
		}
	}
}
